// src/initState.js

import { Dep } from "./Dep"

export default function initState(vm) {
  let data = vm.$options.data

  // data为函数则执行
  // 建议data为函数，防止变量互相污染
  data = vm._data = typeof data === "function" ? data.call(vm, vm) : data || {}

  const keys = Object.keys(data)

  let i = keys.length
  while (i--) {
    // 变量代理
    // 这样做的好处就是操作data里的变量时，只需要this.xxx而不用this.data.xxx
    proxy(vm, "_data", keys[i])
  }
  observe(data)
}

class Observer {
  constructor(value) {
    this.walk(value)
  }

  walk(data) {
    let keys = Object.keys(data)
    // 遍历data的key，并进行响应式判断处理
    for (let i = 0; i < keys.length; i++) {
      defineReactive(data, keys[i], data[keys[i]])
    }
  }
}

function defineReactive(data, key, value) {
  // 每个对象都有自己dep
  const dep = new Dep()
  Object.defineProperty(data, key, {
    get() {
      if (Dep.target) {
        // 如果Dep.target指向某个Watcher，则把此Watcher收入此dep的队列里
        dep.depend()
      }
      return value
    },
    set(newVal) {
      // 设置值时，如果相等则返回
      if (newVal === value) return
      value = newVal
      // 新设置的值也需要响应式判断处理
      observe(newVal)

      // 通知dep里的所有Wacther进行传达更新
      dep.notify()
    },
  })

  // 递归，因为可能对象里有对象
  observe(value)
}

function observe(data) {
  // 只有当data为数组或者对象时才进行响应式处理
  if (typeof data === "object" && data !== null) {
    return new Observer(data)
  }
}

// 代理函数
function proxy(vm, source, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm[source][key]
    },
    set(newVal) {
      return (vm[source][key] = newVal)
    },
  })
}
